package pkg.iss.model;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Timer;

public class Vehicle {
	
        public static final int TIME_OUT = 60000;
        private ThreadPoolExecutor threadPool = new ThreadPoolExecutor(15,50,2,TimeUnit.MINUTES,
                new ArrayBlockingQueue<Runnable>(10));
        private HashMap<SocketAddress, Client> vehicles = new HashMap<SocketAddress, Client>();
        private InetAddress BroadcastIPAddress;
	private DatagramSocket server;	
	private DatagramPacket sendpacket;
	private DatagramPacket receivePacket;
	private String initMsg = "START";
        private Vehicle mainServer = this;
        private int vehicleNumber = 0;
        
	public void start() {                
		try {
                        BroadcastIPAddress = InetAddress.getByName("192.168.2.255");
			server = new DatagramSocket(4455);
                        log("Vehicle " + server.getLocalSocketAddress() + " running...");						
			sendStart(BroadcastIPAddress, initMsg);
			//log("Send START msgm....");
		//================================================================================
		//	server = new DatagramSocket(4455);
			
			byte[] receiveData = new byte[1024];
			
				while(true) {
					log("Waiting msgs....");
					receivePacket = new DatagramPacket(receiveData, receiveData.length);
					server.receive(receivePacket);
					String msg = new String(receivePacket.getData());                                            
					System.out.println("RECEIVED: " + msg + " FROM ");
                                            
                                            switch(msg) {
                                                
                                                case "start":
                                                    
                                                    break;
                                                default:
                                                    
                                            }
				}
				
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
        
        /**
         * go through every single client and send them a message
         * @param buffer message
         */
        void sendToAll(byte[]buffer){
                Iterator<Client> c = vehicles.values().iterator();
                for(int i=0;c.hasNext();i++){
                        try {
                                c.next().sendMessage(buffer);
                        } catch (IOException e) {}
                }
        }
       
        void sendPacket(DatagramPacket packet) throws IOException{
                server.send(packet);
        }
        
        public void sendStart(InetAddress IPAddress, String msg) {
            byte[] data = new byte[1024];
            data = msg.getBytes();
            DatagramPacket packet = new DatagramPacket(data, data.length, IPAddress, 4455);
            try {
                server.send(packet);
                log("Message " + msg.toUpperCase() + " sent.");
            } catch (IOException ex) {
                Logger.getLogger(Vehicle.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        /**
        * Exibe informações no console
        * @param msgm 
        */
        private static void log(String msgm) {
           SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
           System.out.println(sdf.format(new Date()).toUpperCase() + " => " + msgm);
       }
        
        /**
         * Executa todas as mensagens na ThreadPoll para permitir que servidor continue ouvindo solicitações
         * @param buffer
         * @param packet
         */
        private void translatePacket(final byte[] buffer, final DatagramPacket packet) {
                threadPool.execute(new Runnable(){
                        public void run(){
                                Client vehicle = vehicles.get(packet.getSocketAddress());
                                if(vehicle == null){
                                        if(!vehicles.containsKey(packet.getSocketAddress())){ //if we can't find the client check if he exists
                                                vehicles.put(packet.getSocketAddress(),new Client(mainServer ,packet, ++vehicleNumber)); // create new Client and index it by IP Address and port number
                                                vehicle = vehicles.get(packet.getSocketAddress());
                                        }
                                }
                                if(vehicle != null){
                                        vehicle.translateMessage(buffer, new String(buffer,0,packet.getLength())); //dispatch message to client
                                }
                        }
                });
        }
        
         /**
         * remove Vehicle da lista
         * @param client
         */
        void destroyClient(Client client){
                client.destroy();
                vehicles.remove(client.getSocketAddress());
        }
}

class Client implements ActionListener {
       
        private Timer timeoutTimer = new Timer(Vehicle.TIME_OUT,this);
        private Vehicle server;
        private DatagramPacket packet; //hold packet so we can reuse to easily send to user
        private int clientNumber;
       
        public Client(Vehicle server, DatagramPacket packet,int clientNumber){
                this.server = server;
                this.packet = packet;
                this.clientNumber = clientNumber;
                timeoutTimer.setRepeats(false);
                timeoutTimer.start();
                System.out.println("Client Accepted: " + packet.getSocketAddress());
        }
       
        void translateMessage(byte[]buffer,String message){
                timeoutTimer.restart(); //reset the timer back to 60000 milliseconds to prevent destruction
                server.sendToAll(buffer); //send recieved message to all clients
                System.out.println(clientNumber + ": " + message);
        }
       
        public void sendMessage(String message) throws IOException{
                sendMessage(message.getBytes());
        }
       
        /**
         *  
         * @param buffer
         * @throws IOException
         */
        public synchronized void sendMessage(byte[]buffer) throws IOException{
                packet.setData(buffer); //change data in the packet buffer
                if(server != null) server.sendPacket(packet);
        }
       
        /**
         *
         * @return usado para encontrar a chave na HashMap
         */
        public SocketAddress getSocketAddress(){
                return packet.getSocketAddress();
        }
       
        /**
         * Destroi totalmente os veiculos (Normalmente chamado após ter terminado o timeout)
         */
        void destroy(){
                System.out.println("Client Removed: Timed Out -> " + packet.getSocketAddress());
                timeoutTimer.stop();
                timeoutTimer.removeActionListener(this);
                timeoutTimer = null;
                server = null;
        }
 
        @Override
        public void actionPerformed(ActionEvent e) {
                if(e.getSource()==timeoutTimer){
                        server.destroyClient(this);
                }
        }
 
}
